/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    can.c
  * @brief   This file provides code for the configuration
  *          of the CAN instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "can.h"

/* USER CODE BEGIN 0 */
#include "string.h"
/* USER CODE END 0 */

CAN_HandleTypeDef hcan1;

/* CAN1 init function */
void MX_CAN1_Init(void)
{

  /* USER CODE BEGIN CAN1_Init 0 */

  /* USER CODE END CAN1_Init 0 */

  /* USER CODE BEGIN CAN1_Init 1 */

  /* USER CODE END CAN1_Init 1 */
  hcan1.Instance = CAN1;
  hcan1.Init.Prescaler = 36;
  hcan1.Init.Mode = CAN_MODE_NORMAL;
  hcan1.Init.SyncJumpWidth = CAN_SJW_2TQ;
  hcan1.Init.TimeSeg1 = CAN_BS1_3TQ;
  hcan1.Init.TimeSeg2 = CAN_BS2_4TQ;
  hcan1.Init.TimeTriggeredMode = ENABLE;
  hcan1.Init.AutoBusOff = DISABLE;
  hcan1.Init.AutoWakeUp = DISABLE;
  hcan1.Init.AutoRetransmission = DISABLE;
  hcan1.Init.ReceiveFifoLocked = DISABLE;
  hcan1.Init.TransmitFifoPriority = DISABLE;
  if (HAL_CAN_Init(&hcan1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN CAN1_Init 2 */

  /* USER CODE END CAN1_Init 2 */

}

void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(canHandle->Instance==CAN1)
  {
  /* USER CODE BEGIN CAN1_MspInit 0 */

  /* USER CODE END CAN1_MspInit 0 */
    /* CAN1 clock enable */
    __HAL_RCC_CAN1_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**CAN1 GPIO Configuration
    PB8     ------> CAN1_RX
    PB9     ------> CAN1_TX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    __HAL_AFIO_REMAP_CAN1_2();

    /* CAN1 interrupt Init */
    HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
  /* USER CODE BEGIN CAN1_MspInit 1 */

  /* USER CODE END CAN1_MspInit 1 */
  }
}

void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{

  if(canHandle->Instance==CAN1)
  {
  /* USER CODE BEGIN CAN1_MspDeInit 0 */

  /* USER CODE END CAN1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_CAN1_CLK_DISABLE();

    /**CAN1 GPIO Configuration
    PB8     ------> CAN1_RX
    PB9     ------> CAN1_TX
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8|GPIO_PIN_9);

    /* CAN1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn);
  /* USER CODE BEGIN CAN1_MspDeInit 1 */

  /* USER CODE END CAN1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
/**
 * @brief 配置CAN过滤器的参数。
 *
 * 此函数用于配置CAN1的过滤器，设置过滤器的工作模式、尺度、标识符、掩码等参数，
 * 并确保过滤器配置成功。
 *
 * @return void 无返回值。
 */
void CAN_Filter_Setting(void)
{
    // 定义一个CAN过滤器配置结构体变量，用于存储CAN1的过滤器配置信息
    CAN_FilterTypeDef  CAN1_sFilterConfig;
    // 将接收到的CAN消息分配到接收FIFO 0
    CAN1_sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
    // 设置过滤器模式为标识符掩码模式，可通过掩码来匹配标识符
    CAN1_sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
    // 设置过滤器尺度为32位，即使用32位的标识符和掩码进行匹配
    CAN1_sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
    // 启用当前的CAN过滤器
    CAN1_sFilterConfig.FilterActivation = ENABLE;
    // 设置从CAN的起始过滤器组编号为14
    CAN1_sFilterConfig.SlaveStartFilterBank = 14;

    // 设置当前要配置的过滤器组编号为0
    CAN1_sFilterConfig.FilterBank = 0;
    // 设置过滤器标识符的高16位为0
    CAN1_sFilterConfig.FilterIdHigh = 0X0000;
    // 设置过滤器标识符的低16位为0
    CAN1_sFilterConfig.FilterIdLow = 0x0000;
    // 设置过滤器掩码的高16位为0，意味着不屏蔽任何位
    CAN1_sFilterConfig.FilterMaskIdHigh = 0x0000;
    // 设置过滤器掩码的低16位为0，意味着不屏蔽任何位
    CAN1_sFilterConfig.FilterMaskIdLow = 0x0000;
    // 循环调用HAL_CAN_ConfigFilter函数，直到CAN过滤器配置成功
    while(HAL_CAN_ConfigFilter(&hcan1, &CAN1_sFilterConfig) != HAL_OK);
}

/**
 * @brief 启动CAN总线并激活接收FIFO 0消息挂起中断。
 *
 * 此函数用于启动CAN1总线，并激活CAN1接收FIFO 0消息挂起的中断通知。
 * 函数会持续尝试启动CAN总线和激活中断，直到操作成功为止。
 *
 * @return void 无返回值。
 */
void CAN_Start_IT(void)
{
    // 持续尝试启动CAN1总线，直到启动成功
    while(HAL_CAN_Start(&hcan1) != HAL_OK);
    // 持续尝试激活CAN1接收FIFO 0消息挂起的中断通知，直到激活成功
    while(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK);
}


/**
 * @brief CAN接收FIFO 0消息挂起回调函数。当CAN接收FIFO 0中有新消息时，HAL库会自动调用此函数。
 *
 * 该函数会检查触发回调的是否为CAN1外设，如果是，则从CAN1的接收FIFO 0中获取消息，
 * 并将消息信息存储到自定义的CAN消息结构体中，最后将该消息放入CAN1的接收消息队列。
 *
 * @param hcan 指向CAN句柄结构体的指针，代表触发回调的CAN外设。
 */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    // 检查触发回调的是否为CAN1外设
    if (hcan->Instance == CAN1)
    {
        // 定义CAN接收消息头结构体变量，用于存储接收到的消息头信息
        CAN_RxHeaderTypeDef CAN1_RxHeader;
        // 定义CAN消息结构体变量，用于存储完整的CAN消息信息
        CAN_Msg_Def message;
        // 从CAN1的接收FIFO 0中获取接收到的消息
        // &hcan1 表示CAN1的句柄，CAN_RX_FIFO0 表示从接收FIFO 0获取消息
        // &CAN1_RxHeader 用于存储消息头信息，message.buff 用于存储消息数据
        HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &CAN1_RxHeader, message.buff);
        // 将接收到的消息长度（存储在消息头的DLC字段）存储在message结构体中
        message.len = CAN1_RxHeader.DLC;
        // 将接收到的扩展标识符（存储在消息头中）存储在message结构体中
        message.extId = CAN1_RxHeader.ExtId;

        message.stdId = CAN1_RxHeader.StdId;

        memcpy(can_recv_data,message.buff,message.len);

    }
}

/**
 * @brief 向CAN1总线发送一条CAN消息。
 *
 * 该函数用于将传入的CAN消息结构体封装成CAN发送头，然后通过CAN1总线发送出去。
 * 在发送过程中，使用临界区来确保发送操作的原子性，防止被其他任务中断。
 *
 * @param message 包含CAN消息信息的结构体，包含扩展标识符、数据长度和数据缓冲区等信息。
 */
void CAN_TX_ExtId_Msg(CAN_Msg_Def message)
{
    // 定义CAN发送消息头结构体变量，用于存储CAN消息的发送头信息
    CAN_TxHeaderTypeDef CAN1_TxHeader;
    // 定义一个变量，用于存储CAN1发送消息时所使用的邮箱编号
    uint32_t CAN1_TX_BOX0;

    // 将传入的CAN消息结构体中的扩展标识符赋值给CAN发送消息头
    CAN1_TxHeader.ExtId = message.extId;
    // 设置CAN标识符类型为扩展标识符，表明使用29位的扩展标识符
    CAN1_TxHeader.IDE = CAN_ID_EXT;
    // 设置CAN帧类型为数据帧，意味着该帧携带实际的数据
    CAN1_TxHeader.RTR = CAN_RTR_DATA;
    // 设置CAN消息的数据长度，即数据缓冲区中有效数据的字节数
    CAN1_TxHeader.DLC = message.len;


    // 调用HAL库函数，将封装好的CAN消息添加到CAN1的发送邮箱并发送出去
    // &hcan1 表示CAN1的句柄，&CAN1_TxHeader 是发送消息头，message.buff 是数据缓冲区，&CAN1_TX_BOX0 用于返回使用的邮箱编号
    HAL_CAN_AddTxMessage(&hcan1, &CAN1_TxHeader, message.buff, &CAN1_TX_BOX0);
}


void CAN_TX_StdId_Msg(CAN_Msg_Def message)
{
    CAN_TxHeaderTypeDef CAN1_TxHeader;

    uint32_t CAN1_TX_BOX0;
    CAN1_TxHeader.StdId = message.stdId;
    CAN1_TxHeader.IDE = CAN_ID_STD;
    CAN1_TxHeader.RTR = CAN_RTR_DATA;
    CAN1_TxHeader.DLC = message.len;

    HAL_CAN_AddTxMessage(&hcan1, &CAN1_TxHeader, message.buff, &CAN1_TX_BOX0);
}
/* USER CODE END 1 */
